package com.phiture.erp.produce.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.phiture.erp.common.constants.ErpNoPrefixConstants;
import com.phiture.erp.common.enums.ErpAuditStatus;
import com.phiture.erp.common.utils.ErpNoGenerator;
import com.phiture.erp.produce.common.enums.MrpReleaseStatusEnum;
import com.phiture.erp.produce.common.enums.MrpTaskIssueStatusEnum;
import com.phiture.erp.produce.core.convert.ErpMaterialRequirementPlanConvertMapper;
import com.phiture.erp.produce.core.pojo.dto.ErpCreateMaterialRequirementPlanDTO;
import com.phiture.erp.produce.core.pojo.dto.ErpCreateMaterialRequirementPlanItemsDTO;
import com.phiture.erp.produce.core.pojo.dto.ErpMaterialRequirementPlanSaveReqDTO;
import com.phiture.erp.produce.core.pojo.dto.ErpMaterialRequirementPlanUpdateStatusDTO;
import com.phiture.erp.produce.core.pojo.vo.ErpMaterialRequirementPlanItemVO;
import com.phiture.erp.produce.dal.pojo.vo.ErpMaterialRequirementPlanVO;
import com.phiture.erp.produce.core.service.ErpMaterialRequirementPlanService;
import com.phiture.erp.produce.dal.entity.ErpMaterialRequirementPlanDO;
import com.phiture.erp.produce.dal.entity.ErpMaterialRequirementPlanItemsDO;
import com.phiture.erp.produce.dal.mapper.ErpMaterialRequirementPlanItemsMapper;
import com.phiture.erp.produce.dal.mapper.ErpMaterialRequirementPlanMapper;
import com.phiture.erp.produce.dal.pojo.dto.ErpMaterialRequirementPlanPageReqDTO;
import com.phiture.erp.produce.dal.pojo.vo.ErpMaterialRequirementPlanRespVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.phiture.erp.common.constants.ErrorCodeConstants.*;

/**
 * 物料需求计划 Service 实现类
 *
 * @author YH
 */
@Service
@RequiredArgsConstructor
public class ErpMaterialRequirementPlanServiceImpl implements ErpMaterialRequirementPlanService {

    private final ErpMaterialRequirementPlanMapper materialRequirementPlanMapper;
    private final ErpMaterialRequirementPlanItemsMapper materialRequirementPlanItemsMapper;
    private final ErpNoGenerator erpNoGenerator;

//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public Long createMaterialRequirementPlan(ErpMaterialRequirementPlanSaveReqDTO createReqDTO) {
//        // 插入
//        ErpMaterialRequirementPlanDO materialRequirementPlan = BeanUtils.toBean(createReqDTO, ErpMaterialRequirementPlanDO.class);
//        materialRequirementPlanMapper.insert(materialRequirementPlan);
//
//        // 插入子表
//        createMaterialRequirementPlanItemsList(materialRequirementPlan.getId(), createReqDTO.getMaterialRequirementPlanItems());
//        // 返回
//        return materialRequirementPlan.getId();
//    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createMaterialRequirementPlan(ErpCreateMaterialRequirementPlanDTO planDTO) {
        String mrpNo = erpNoGenerator.generate(ErpNoPrefixConstants.MRP_NO_PREFIX);
        validateMrpNo(mrpNo);
        // 表头
        ErpMaterialRequirementPlanDO planDO = new ErpMaterialRequirementPlanDO();
        planDO.setStatus(ErpAuditStatus.APPROVE.getStatus()).setMrpNo(mrpNo)
                .setSourceBillId(planDTO.getSourceBillId()).setSourceBillNo(planDTO.getSourceBillNo())
                .setIssuedStatus(MrpTaskIssueStatusEnum.NOT_YET_ISSUED.getStatus());
        materialRequirementPlanMapper.insert(planDO);
        // 条目表
        createMaterialRequirementPlanItems(planDO.getId(), planDTO.getItems());
    }

    private void validateMrpNo(String mrpNo){
        boolean exists = materialRequirementPlanMapper.exists(
                Wrappers.lambdaQuery(ErpMaterialRequirementPlanDO.class)
                        .eq(ErpMaterialRequirementPlanDO::getMrpNo, mrpNo)
                        .eq(ErpMaterialRequirementPlanDO::getDeleted, Boolean.FALSE)
        );
        if(exists){
            throw exception(MRP_NO_EXISTS);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMaterialRequirementPlan(ErpMaterialRequirementPlanSaveReqDTO updateReqDTO) {
        // 校验存在
        validateMaterialRequirementPlanExists(updateReqDTO.getId());
        // 更新
        ErpMaterialRequirementPlanDO updateObj = BeanUtils.toBean(updateReqDTO, ErpMaterialRequirementPlanDO.class);
        materialRequirementPlanMapper.updateById(updateObj);

        // 更新子表
        updateMaterialRequirementPlanItemsList(updateReqDTO.getId(), updateReqDTO.getMaterialRequirementPlanItems());
    }

    @Override
    public void updateStatus(ErpMaterialRequirementPlanUpdateStatusDTO updateReqDTO) {
        // 有“采购申请单”或者“生产任务”生成则禁止【反审批】
        if(ErpAuditStatus.PROCESS.equals(updateReqDTO.getStatus())
        && materialRequirementPlanItemsMapper.existsReleasedData(updateReqDTO.getId())){
            throw exception(MRP_ANTI_APPROVAL_FAIL_EXISTS_RELEASED);
        }
        materialRequirementPlanMapper.updateById(
                new ErpMaterialRequirementPlanDO().setId(updateReqDTO.getId()).setStatus(updateReqDTO.getStatus())
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMaterialRequirementPlan(Long id) {
        // 校验存在
        validateMaterialRequirementPlanExists(id);
        // 删除
        materialRequirementPlanMapper.deleteById(id);

        // 删除子表
        deleteMaterialRequirementPlanItemsByMaterialRequirementPlanId(id);
    }

    private void validateMaterialRequirementPlanExists(Long id) {
        if (materialRequirementPlanMapper.selectById(id) == null) {
            throw exception(MATERIAL_REQUIREMENT_PLAN_NOT_EXISTS);
        }
    }

    @Override
    public ErpMaterialRequirementPlanDO getMaterialRequirementPlan(Long id) {
        return materialRequirementPlanMapper.selectById(id);
    }

    @Override
    public PageResult<ErpMaterialRequirementPlanRespVO> getMaterialRequirementPlanPage(ErpMaterialRequirementPlanPageReqDTO pageReqDTO) {
        IPage<ErpMaterialRequirementPlanRespVO> page = materialRequirementPlanMapper.pageMaterialRequirementPlan(
                Page.of(pageReqDTO.getPageNo(), pageReqDTO.getPageSize()),
                pageReqDTO.getSourceBillNo(),
                pageReqDTO.getMrpNo(),
                pageReqDTO.getSaleOrderNo(),
                pageReqDTO.getStatus(),
                ArrayUtils.get(pageReqDTO.getCreateTime(), 0),
                ArrayUtils.get(pageReqDTO.getCreateTime(), 1)
        );
        return PageResult.of(page.getRecords(), page.getTotal());
    }

    @Override
    public ErpMaterialRequirementPlanVO getInfoById(Long id) {
        ErpMaterialRequirementPlanVO info = materialRequirementPlanMapper.getInfoById(id);
        if(info == null) return null;
        info.setStatusDesc(ErpAuditStatus.fromStatus(info.getStatus()).getName());
        info.setIssuedStatusDesc(MrpTaskIssueStatusEnum.fromStatus(info.getIssuedStatus()).getDesc());
        return info;
    }

    @Override
    public List<ErpMaterialRequirementPlanItemVO> getItemInfo(Collection<Long> ids) {
        List<ErpMaterialRequirementPlanItemsDO> items = materialRequirementPlanItemsMapper.selectListByMaterialRequirementPlanIds(ids);
        return ErpMaterialRequirementPlanConvertMapper.INSTANCE.toErpMaterialRequirementPlanItemVOList(items);
    }

    @Override
    public List<ErpMaterialRequirementPlanItemsDO> getByItemIds(Collection<Long> itemIds) {
        return materialRequirementPlanItemsMapper.selectBatchIds(itemIds);
    }

    @Override
    public void backfillItemsStatusToReleased(Collection<Long> itemIds) {
        materialRequirementPlanItemsMapper.update(
                Wrappers.lambdaUpdate(ErpMaterialRequirementPlanItemsDO.class)
                        .set(ErpMaterialRequirementPlanItemsDO::getStatus, MrpReleaseStatusEnum.RELEASED.getStatus())
                        .eq(ErpMaterialRequirementPlanItemsDO::getDeleted, Boolean.FALSE)
                        .in(ErpMaterialRequirementPlanItemsDO::getId, itemIds)
        );
    }

    @Override
    public void updateIssuedStatus(Long materialRequirementPlanId, Integer issuedStatus) {
        materialRequirementPlanMapper.updateById(new ErpMaterialRequirementPlanDO().setId(materialRequirementPlanId).setIssuedStatus(issuedStatus));
    }

    @Override
    public void changeIssuedStatus(Collection<Long> materialRequirementPlanIds) {
        List<ErpMaterialRequirementPlanItemsDO> items = materialRequirementPlanItemsMapper
                .selectListByMaterialRequirementPlanIds(materialRequirementPlanIds);

        if (CollUtil.isEmpty(items)) {
            return;
        }

        MrpTaskIssueStatusEnum statusEnum = determineIssueStatus(items);

        for (Long materialRequirementPlanId : materialRequirementPlanIds) {
            materialRequirementPlanMapper.updateById(
                    new ErpMaterialRequirementPlanDO()
                            .setId(materialRequirementPlanId)
                            .setIssuedStatus(statusEnum.getStatus())
            );
        }
    }

    @Override
    public Map<Long, String> getErpMaterialRequirementPlanMap(Collection<Long> ids) {
        List<ErpMaterialRequirementPlanDO> list = materialRequirementPlanMapper.selectBatchIds(ids);
        return CollUtil.isEmpty(list)
                ? Collections.emptyMap()
                : list.stream().collect(Collectors.toMap(ErpMaterialRequirementPlanDO::getId, ErpMaterialRequirementPlanDO::getSourceBillNo));
    }

    /**
     * 根据物料项状态确定主计划下达状态
     */
    private MrpTaskIssueStatusEnum determineIssueStatus(List<ErpMaterialRequirementPlanItemsDO> items) {
        Set<Integer> statusSet = items.stream()
                .map(ErpMaterialRequirementPlanItemsDO::getStatus)
                .collect(Collectors.toSet());

        if (statusSet.containsAll(Arrays.asList(
                MrpReleaseStatusEnum.UNRELEASED.getStatus(),
                MrpReleaseStatusEnum.RELEASED.getStatus()))) {
            return MrpTaskIssueStatusEnum.PARTIALLY_ISSUED;
        }

        Integer singleStatus = statusSet.iterator().next();
        return singleStatus.equals(MrpReleaseStatusEnum.UNRELEASED.getStatus())
                ? MrpTaskIssueStatusEnum.NOT_YET_ISSUED
                : MrpTaskIssueStatusEnum.ALL_ISSUED;
    }

    private void createMaterialRequirementPlanItemsList(Long materialRequirementPlanId, List<ErpMaterialRequirementPlanItemsDO> list) {
        list.forEach(o -> o.setMaterialRequirementPlanId(materialRequirementPlanId));
        materialRequirementPlanItemsMapper.insertBatch(list);
    }

    private void createMaterialRequirementPlanItems(Long materialRequirementPlanId, List<ErpCreateMaterialRequirementPlanItemsDTO> items) {
        List<ErpMaterialRequirementPlanItemsDO> list = ErpMaterialRequirementPlanConvertMapper.INSTANCE.toErpMaterialRequirementPlanItemsDOList(items);
        list.forEach(o -> o.setMaterialRequirementPlanId(materialRequirementPlanId));
        materialRequirementPlanItemsMapper.insertBatch(list);
    }

    private void updateMaterialRequirementPlanItemsList(Long materialRequirementPlanId, List<ErpMaterialRequirementPlanItemsDO> list) {
        deleteMaterialRequirementPlanItemsByMaterialRequirementPlanId(materialRequirementPlanId);
		list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新
        createMaterialRequirementPlanItemsList(materialRequirementPlanId, list);
    }

    private void deleteMaterialRequirementPlanItemsByMaterialRequirementPlanId(Long materialRequirementPlanId) {
        materialRequirementPlanItemsMapper.deleteByMaterialRequirementPlanId(materialRequirementPlanId);
    }

}